package com.keywaysoft.workflow.service.impl;

import com.keywaysoft.workflow.constants.WorkflowConstants;
import com.keywaysoft.workflow.service.IWorkFlowService;
import com.keywaysoft.workflow.utils.ListUtil;
import com.keywaysoft.workflow.utils.ProcessKit;
import com.keywaysoft.workflow.utils.R;
import com.keywaysoft.workflow.utils.exception.BizException;
import com.keywaysoft.workflow.vo.DeploymentVO;
import com.keywaysoft.workflow.vo.ProcessDefinitionVO;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.zip.ZipInputStream;

/**
 * @author zhaojx
 */
@Slf4j
@Service
public class WorkFlowServiceImpl implements IWorkFlowService {
    @Resource
    private RepositoryService repositoryService;

    @Override
    public R uploadStreamAndDeployment(MultipartFile multipartFile, String name) {
        // 获取上传的文件名
        String fileName = multipartFile.getOriginalFilename();
        //如果流程部署名为空则默认流程部署名
        if (com.mysql.cj.util.StringUtils.isNullOrEmpty(name)) {
            name = fileName.concat(UUID.randomUUID().toString().replaceAll(ProcessKit.FLOW_SPLIT, ""));
        }

        try {
            // 得到输入流（字节流）对象
            InputStream fileInputStream = multipartFile.getInputStream();
            // 文件的扩展名
            String extension = FilenameUtils.getExtension(fileName);
            Deployment deployment = null;
            if (ProcessKit.FILE_TYPE.equals(extension)) {
                ZipInputStream zip = new ZipInputStream(fileInputStream);
                //初始化流程
                deployment = repositoryService.createDeployment()
                        .addZipInputStream(zip)
                        .name(name)
                        .deploy();
            } else {
                //初始化流程
                deployment = repositoryService.createDeployment()
                        .addInputStream(fileName, fileInputStream)
                        .name(name)
                        .deploy();
            }
            return R.success(deployment.getId() + ProcessKit.FLOW_SPLIT + fileName);
        } catch (Exception e) {
            return R.fail(new BizException(WorkflowConstants.IS_SUSPENDED));
        }
    }

    @Override
    public R addDeploymentByString(String stringBPMN, String name) {
        if (StringUtils.isBlank(name)) {
            name = "部署流程" + UUID.randomUUID().toString().replaceAll("-", "");
        }
        try {
            Deployment deployment = repositoryService.createDeployment()
                    .addString("CreateWithBPMNJS.bpmn", stringBPMN)
                    .name(name)
                    .deploy();
            return R.success(deployment.getId());
        } catch (Exception e) {
            return R.fail(new BizException(WorkflowConstants.DEPLOYMENT_FAILURE));
        }
    }

    @Override
    public R getDeployments() {
        try {
            List<DeploymentVO> resultList = new ArrayList<DeploymentVO>();
            List<Deployment> list = repositoryService.createDeploymentQuery().list();
            for (Deployment dep : list) {
                DeploymentVO vo = new DeploymentVO();
                vo.setId(dep.getId());
                vo.setDeploymentTime(dep.getDeploymentTime());
                vo.setName(dep.getName());
                vo.setKey(dep.getKey());
                resultList.add(vo);
            }
            return R.success(resultList);
        } catch (Exception e) {
            return R.fail(new BizException(WorkflowConstants.QUERY_FAILURE));
        }
    }

    @Override
    public R getDefinitions() {
        try {
            List<ProcessDefinitionVO> resultList = new ArrayList<ProcessDefinitionVO>();
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionVersion().desc().list();
            if (ListUtil.isEmpty(list)) {
                return R.success(resultList);
            }
            list.stream().forEach(p -> {
                ProcessDefinitionVO vo = new ProcessDefinitionVO();
                vo.setProcessDefinitionId(p.getId());
                vo.setKey(p.getKey());
                vo.setName(p.getName());
                vo.setProcessSuspended(p.isSuspended());
                vo.setVersion(p.getVersion());
                vo.setDeploymentId(p.getDeploymentId());
                vo.setResourceName(p.getResourceName());
                resultList.add(vo);
            });
            return R.success(resultList);
        } catch (Exception e) {
            return R.fail(new BizException(WorkflowConstants.QUERY_FAILURE));
        }
    }

    @Override
    public void getProcessDefineXML(HttpServletResponse response, String deploymentId) {
        if (StringUtils.isBlank(deploymentId)) {
            new BizException(WorkflowConstants.PARAM_ERROR);
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().
                deploymentId(deploymentId).
                singleResult();
        if (Objects.isNull(processDefinition)) {
            throw new BizException(WorkflowConstants.NO_DEP);
        }
        try {
            InputStream inputStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
            int count = inputStream.available();
            byte[] bytes = new byte[count];
            response.setContentType("text/xml");
            OutputStream outputStream = response.getOutputStream();
            while (inputStream.read(bytes) != -1) {
                outputStream.write(bytes);
            }
            inputStream.close();
        } catch (Exception e) {
            e.toString();
        }
    }

    @Override
    public void getProcessDefinePng(String deploymentId, HttpServletResponse response) throws IOException {
        if (StringUtils.isBlank(deploymentId)) {
            new BizException(WorkflowConstants.PARAM_ERROR);
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().
                deploymentId(deploymentId).
                singleResult();
        if (Objects.isNull(processDefinition)) {
            throw new BizException(WorkflowConstants.NO_DEP);
        }
        // 流程定义
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        ProcessDiagramGenerator ge = new DefaultProcessDiagramGenerator();
        InputStream imageStream = null;
        try {
            imageStream = ge.generateDiagram(bpmnModel, "png",
                    //                runtimeService.getActiveActivityIds(task.getExecutionId()),
                    new ArrayList<>(),
                    new ArrayList<String>(), "宋体", "宋体", null, 1.0d);
            response.setContentType("image/jpeg");
            byte[] b = new byte[1024];
            int len;
            while ((len = imageStream.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().close();
            if (imageStream != null) {
                imageStream.close();
            }
        }
    }


    @Override
    public R suspendAllProcessInstance(String key) {
        //1. 查询流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(key)
                .singleResult();

        return suspend(processDefinition);
    }

    @Override
    public R suspendAllProcessInstanceByID(String depID) {
        //1. 查询流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(depID)
                .singleResult();
        return suspend(processDefinition);
    }

    private R suspend(ProcessDefinition processDefinition) {
        //2.获取当前流程定义的实例是否是挂起状态
        boolean suspended = processDefinition.isSuspended();

        //3.获取流程定义的ID
        String processDefinitionId = processDefinition.getId();

        //4.如果是挂起状态则激活
        if (suspended) {
            //如果挂起则进行激活操作,第一个参数是流程定义的ID，参数二传是否激活，参数3传激活时间
            repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
            log.info("流程定义ID:{} 已经激活", processDefinitionId);
            return R.success().setMsg(WorkflowConstants.FLOW_IS_ACTIVE);
        } else {
            //5.如果是激活状态则挂起,第一个参数是流程定义的ID，参数二 是否暂停，参数3 暂停时间
            repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
            log.info("流程定义ID:{} 已经挂起", processDefinitionId);
            return R.success().setMsg(WorkflowConstants.FLOW_IS_SUSPEND);
        }
    }

    @Override
    public R delDefinition(@RequestParam("depID") String depID) {
        repositoryService.deleteDeployment(depID, true);
        return R.success();
    }

}
